/*	--- 集成Hibernate ---


[使用JdbcTemplate]的时候，我们【用得最多的方法】就是
	List<T> query(String sql, Object[] args, RowMapper rowMapper)

【这个RowMapper的作用】就是【把ResultSet的一行记录】映射为【Java Bean】。
(sping中的RowMapper可以将数据中的每一行数据封装成用户定义的类。)


这种把【关系数据库的表记录】映射为【Java对象】的【过程】就是
	ORM：Object-Relational Mapping 对象关系映射

ORM既可以把【记录】转换成【Java对象】，也可以把【Java对象】转换为【行记录】。


【使用JdbcTemplate】 配合 【RowMapper】 可以看作是【最原始的ORM】。

如果要实现【更自动化的ORM】，可以选择[成熟的ORM框架]，例如[Hibernate 冬眠,蛰伏]。


-------------------------------


我们来看看[如何在 Spring中 集成Hibernate]。

Hibernate 作为【ORM框架】，它可以【替代JdbcTemplate】，但【Hibernate】仍然需要【JDBC驱动】

所以，我们需要【引入JDBC驱动、连接池，以及Hibernate本身】。

在Maven中，我们加入【以下依赖项】：
-------------------------------------------------

	<!-- JDBC驱动，这里使用HSQLDB -->
	<dependency>
	    <groupId>org.hsqldb</groupId>
	    <artifactId>hsqldb</artifactId>
	    <version>2.5.0</version>
	</dependency>

	<!-- JDBC连接池 -->
	<dependency>
	    <groupId>com.zaxxer</groupId>
	    <artifactId>HikariCP</artifactId>
	    <version>3.4.2</version>
	</dependency>

	<!-- Hibernate -->
	<dependency>
	    <groupId>org.hibernate</groupId>
	    <artifactId>hibernate-core</artifactId>
	    <version>5.4.2.Final</version>
	</dependency>

	<!-- Spring Context和Spring ORM -->
	<dependency>
	    <groupId>org.springframework</groupId>
	    <artifactId>spring-context</artifactId>
	    <version>5.2.0.RELEASE</version>
	</dependency>

	<dependency>
	    <groupId>org.springframework</groupId>
	    <artifactId>spring-orm</artifactId>
	    <version>5.2.0.RELEASE</version>
	</dependency>

---------------------------------------------------


在[AppConfig]中

我们仍然需要
	· 创建DataSource
	· 引入JDBC配置文件
	· 启用声明式事务

： */
@Configuration 
@ComponentScan 
@EnableTransactionManagement 
@PropertySource("jdbc.properties") 
public class AppConfig { 
	@Bean 
	DataSource createDataSource() {
		//...
	}
}


/*
为了【启用Hibernate】，我们需要【创建一个 LocalSessionFactoryBean 】： */
public class AppConfig {
	@Bean 
	LocalSessionFactoryBean createSessionFactory(@Autowired DataSource dataSource) {

		var props = new Properties();

		props.setProperty("hibernate.hbm2dd1.auto", "update"); // 生产环境不要用
		props.setProperty("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
		props.setProperty("hibernate.show_sql", "true");


		var sessionFactoryBean = new LocalSessionFactoryBean();

		sessionFactoryBean.setDataSource(dataSource);
	// 扫描 指定的package获取所有entity class
		sessionFactoryBean.setPackagesToScan("com.itranswarp.learnjava.entity");
		sessionFactoryBean.setHibernatePropreties(props);

		return sessionFactoryBean;
	}
}

/*
注意我们在[定制Bean]中, 讲到过[FactoryBean]

[LocalSessionFactoryBean]是[一个FactoryBean]，它会再[自动创建一个SessionFactory]

在[Hibernate]中，
	[Session]是[封装了一个JDBC Connection的实例]
	而[SessionFactory]是[封装了JDBC DataSource的实例]

即[SessionFactory]持有[连接池]，每次【需要操作数据库的时候】，【SessionFactory】创建【一个新的Session】，相当于从【连接池】获取到【一个新的Connection】。

【SessionFactory (持有连接池DataSource) 】就是【Hibernate】提供的【最核心的一个对象】

但【LocalSessionFactoryBean】是【Spring】提供的【为了让我们 方便创建SessionFactory 的类】。

---------------------

注意到【上面创建LocalSessionFactoryBean的代码】，首先用【Properties】持有【Hibernate】初始化【SessionFactory】时用到的【所有设置】，【常用的设置】请参考【Hibernate文档】，这里我们只定义了【3个设置】：

	1. hibernate.hbm2ddl.auto=update：表示【自动创建数据库的表结构】，注意【不要在生产环境中启用】；

	2. hibernate.dialect=org.hibernate.dialect.HSQLDialect：指示【Hibernate使用的数据库是[HSQLDB]。 】
	【Hibernate】使用【一种HQL的查询语句】。 它和SQL类似，但真正在“翻译”成SQL时，会【根据设定的数据库“方言”】来生成【针对数据库优化的SQL】；

	3. hibernate.show_sql=true：让【Hibernate】打印【执行的SQL】，这对于【调试】非常有用，我们可以方便地看到【Hibernate】生成的【SQL语句】是否符合【我们的预期】。


-------------------

除了【设置 DataSource和Properties】之外，注意到【setPackagesToScan()】我们传入了【一个package名称】，它指示【Hibernate】扫描【这个包下面的所有Java类】，自动找出【能映射为"数据库表记录"的[JavaBean] 】。

后面我们会仔细讨论【如何编写"符合Hibernate要求"的JavaBean】。

紧接着，我们还需要【创建HibernateTemplate 以及 创建HibernateTransactionManager】： */
public class AppConfig {
	@Bean 
	HibernateTemplate createHibernateTemplate(@Autowired SessionFactory sessionFactory) {
		return new HibernateTemplate(sessionFactory);
	}

	@Bean 
	PlatformTransactionManager createTxManager(@Autowired SessionFactory sessionFactory) {
		return new HibernateTransactionManager(sessionFactory);
	}

	//...
}


/*
【这2个Bean的创建】都十分简单。

【HibernateTransactionManager】是【配合Hibernate】使用【声明式事务】所【必须的】

而【HibernateTemplate】则是【Spring】为了便于【我们使用Hibernate提供的"工具类"】，"不是非用不可"，但推荐【使用以简化代码】。

-----------------

到此为止，【所有的配置】都定义完毕，我们来看看如何将【数据库表结构】映射为【Java对象】。

考察【如下的数据库表】：
--------------------------------
CREATE TABLE user 
	id BIGINT NOT NULL AUTO_INCREMENT, 
	email VARCHAR(100) NOT NULL, 
	password VARCHAR(100) NOT NULL, 
	name VARCHAR(100) NOT NULL, 
	createdAt BIGINT NOT NULL, 

	PRIMARY KEY ('id'),
	UNIQUE KEY 'email' ('email')
);

---------------------------------


其中，
	id 是 AUTO_INCREMENT(自增主键)
	email、password、name 是 VARCHAR(100)，
	email 带 UNIQUE KEY 'email' 唯一索引以确保唯一性
	createdAt 存储 BIGINT 整型类型的时间戳

---------

用 JavaBean表示 如下： */
public class User {
	private Long id;
	private String email;
	private String password;
	private String name;
	private Long createdAt;

// getters and setters
	//...
}


/*
【这种映射关系】十分易懂

但我们需要【添加一些注解】来告诉【Hibernate】如何把【User类】映射到【表记录】： */
@Entity // 创建一个Entity Bean对象相当于新建一条记录，容器会自动将Entity Bean的状态和数据库同步。
public class User {
	@Id 
	@GeneratedValue(strategy = GenerationType.IDENTITY) 
	@Column(nullable = false, updatable = false) 
	public Long getId() {
		//...
	}

	@Column(nullable = false, unique = true, length = 100) 
	public String getEmail() {
		//...
	}

	@Column(nullable = false, length = 100) 
	public String getPassword() {
		//...
	}

	@Column(nullable = false, length = 100) 
	public String getName() {
		//...
	}

	@Columnt(nullable = false, updatable = false) 
	public Long getCreatedAt() {
		//...
	}
}

/*
如果【一个JavaBean】被用于【(数据库Table)映射】，我们就【标记一个 @Entity 】。

-----------------

默认情况下，【映射的表名】是【user】，如果【实际的表名】不同，

例如:【实际表名】是[users]，可以追加一个 @Table(name="users") 表示： */
@Entity 
@Table(name = "users")
public class User {
	//...
}

/*
【每个属性】到【数据库Column的映射】用 @Column() 标识

	nullable 指示Column(列) 是否允许为NULL

	updatable 指示 该Column(列) 是否允许被用在UPDATE语句

	length 指示 String类型的Column(列)的长度（如果没有指定，默认是255）。


对于[主键]，还需要用 @Id 标识

【自增主键】再追加一个 @GeneratedValue ，以便【Hibernate】能读取到【自增主键的值】。

----------------------------

细心的童鞋可能还注意到，【主键id 定义的类型】不是[long]，而是[Long]。

这是因为【Hibernate】如果【检测到 主键=null】，就【不会在INSERT语句中 指定 主键的值】，而是【返回 由[数据库]生成的[自增值]】

否则，【Hibernate】认为【我们的程序】指定了【主键的值】，会在【INSERT语句中 直接列出】。

------

【long型字段】总是具有【默认值0】，因此，【每次插入的主键值】总是【0】，导致【除第一次外后续插入】都将【失败】。

【createdAt】虽然是【整型】，但我们【并没有使用long】，而是【Long】

这是因为：使用【基本类型】会导致【某种查询】会添加【意外的条件】

后面我们会详细讨论，这里【只需牢记】，作为【映射使用的JavaBean】，【所有属性】都使用【包装类型】而不是【基本类型】。


PS:  使用【Hibernate】时，不要使用[基本类型的属性]，总是使用[包装类型]，如[Long]或[Integer]。


=============================================

类似的，我们再定义【一个Book类】： */
@Entity 
public class Book {
	@Id 
	@GeneratedValue(strategy = GenerationType.IDENTITY) 
	@Column(nullable = false, updatable = false) 
	public Long getId() {
		//...
	}

	@Column(nullable = false, length = 100) 
	public String getTitle() {
		//...
	}

	@Column(nullable = false, updatable = false) 
	public Long getCreateAt() {
		//...
	}
}

/*
如果仔细观察User和Book

会发现它们定义的 [id、createdAt属性] 是一样的，这在[数据库表结构的设计]中很常见：
	对于【每个表】，通常我们会【统一使用 一种主键生成机制】，并添加【createdAt】表示【创建时间】，【updatedAt】表示【修改时间等】通用字段。

-----------------------

不必在[User和Book]中【重复定义这些通用字段】，我们可以把【它们(重复定义的Field)】提到[一个抽象类]中： */
@MappedSuperclass 
public abstract class AbstractEntity {

	private Long id;
	private Long createdAt;

	@Id 
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(nullable = false, updatable = false) 
	public Long getId() {
		//...
	}

	@Transient 
	public ZonedDateTime getCreatedDateTime() {
		return Instant.ofEpochMilli(this.createdAt).atZone(ZoneId.systemDefault());
	}

	@PrePersist 
	public void preInsert() {
		setCreatedAt(System.currentTimeMillis());
	}
}

/*
对于【AbstractEntity】来说，我们要标注一个 @MappedSuperclass 表示 【它用于继承】。

此外，注意到我们定义了一个 
	@Transient 方法, 
它【返回 一个“虚拟”的属性】。

因为 
	getCreatedDateTime() 
是【计算得出的属性，而不是从[数据库表]读出的值】，因此必须要标注
	@Transient ,
否则【Hibernate】会尝试从[数据库]读取【名为createdDateTime】这个【不存在的字段】从而【出错】。


再注意到
	@PrePersist 标识的方法，
它表示在我们将[一个JavaBean]持久化到[数据库之前]（即执行INSERT语句），
【Hibernate】会先执行【该方法】，这样我们就可以【自动设置好createdAt属性】。

----------------------

有了【AbstractEntity】，我们就可以【大幅简化 User 和 Book】： */
@Entity 
public class User extends AbstractEntity {
	@Column(nullable = false, unique = true, length = 100) 
	public String getEmail() {
		//...
	}

	@Column(nullable = false, length = 100) 
	public String getPassword() {
		//...
	}

	@Column(nullable = false, length = 100) 
	public String getName() {
		//...
	}
}

/*
注意到【使用的所有注解】均来自[javax.persistence]，它是JPA规范的一部分。

这里我们只介绍[使用注解的方式]配置[Hibernate映射关系]，不再介绍[传统的比较繁琐的XML配置]。

通过[Spring]集成[Hibernate]时，也不再需要[hibernate.cfg.xml配置文件]

用一句话总结： 
    使用Spring集成Hibernate，配合[JPA注解]，无需任何额外的XML配置。
	(JPA, Java Persistence API)

=======================================================


类似[User、Book]这样的用于[ORM的Java Bean]，我们通常称之为[Entity Bean]。

最后，我们来看看【如果对user表进行增删改查】。

因为【使用了Hibernate】，因此，我们要做的，实际上是对[User]这个[JavaBean]进行“增删改查”。

我们编写[一个UserService]，【注入HibernateTemplate】以便【简化代码】： */
@Component 
@Transactional 
public class UserService {
	@Autowired 
	HibernateTemplate hibernateTemplate;
}

/*======================================================


#	Insert操作

要【持久化[一个User实例]】，我们只需调用[save()方法]。

以 register()方法 为例，代码如下： */
public User register(String email, String password, String name) {
	// 创建一个User对象
	User user = new User();

	// set Attribute
	user.setEmail(email);
	user.setPassword(password);
	user.setName(name);
		// 不要设置id, 因为使用了 AUTO_INCREMENT

	// 保存到[数据库]
	hibernateTemplate.save(user);

	// 保存后,则自动获得了id
	System.out.println(user.getId());

	return user;
}


/*======================================================


#	Delete操作

【删除一个User】相当于【从表中删除对应的记录】。

注意[Hibernate]总是用[id]来【删除记录】

因此，【要正确设置User的id属性】才能【正常删除记录】： */
public boolean deleteUser(Long id) {
	User user = hibernateTemplate.get(User.class, id);
		// .get(X.class, id)

	if (user != null) {
		hibernateTemplate.delete(user); 
			// hibernateTemplate.delete(User);
		return true;
	}

	return false;
}

/*
通过[主键]删除记录时，一个常见的用法是
	1. 先根据主键加载该记录
		User user = hibernateTemplate.get(User.class, id);
	2. 再删除
		hibernateTemplate.delete(user);

load() 和 get() 都可以【根据[主键]加载[记录] 】

它们的区别在于，当[记录]不存在时:
	· get() 返回 null
	· load()抛出 异常

============================================================


#	Update操作

[更新记录]相当于【先更新User的指定属性】，然后【调用update()方法】： */
public void updateUser(Long id, String name) {
	User user = hibernateTemplate.load(User.class, id);
	user.setName(name);
	hibernateTemplate.update(user);
}

/*
前面我们在【定义User】时，对【有的属性】标注了 
	@Column(updatable=false)
可以防止不小心更新数据

[Hibernate]在【更新记录时】，它只会把[@Column(updatable=true)的属性] 加入到 [UPDATE语句]中，这样可以提供[一层额外的安全性]

即如果【不小心修改了User的email、createdAt等属性】，【执行update()时】并不会更新【对应的数据库列】。

但也必须牢记：这个功能是[Hibernate]提供的，如果[绕过Hibernate]直接【通过JDBC执行UPDATE语句】仍然可以【更新"数据库"的"任意列的值"】。

----------------------------------------

最后，我们编写的大部分方法都是【各种各样的查询】。

【根据id查询】我们可以直接调用
	load()
	get()

如果要使用条件查询，有[3种方法]。


假设我们想执行以下查询：

	SELECT * FROM user WHERE email = ? AND password = ?

我们来看看可以使用什么查询。


============================================================

#	使用Example查询

[第1种方法]是使用:
	findByExample()

给出[一个User实例]，[Hibernate]把[该实例の所有非null的属性]拼成[WHERE条件]： */
public User login(String email, String password) {
	User example = new User();

	example.setEmail(email);
	example.setPassword(password);

	List<User> list = hibernateTemplate.findByExample(example);
		// WHERE email = ? AND password = ?

	return list.isEmpty() ? null : list.get(0);
}


/*
因为[example实例]只有
	email
	password
这两个属性为[非null]，所以最终生成的[WHERE语句]就是
	WHERE email = ? AND password = ?

----------

如果我们把[User的createdAt的类型]从[Long]改为[long]

[findByExample() 的查询]将出问题

原因: 在于[example实例]的[long类型Field createdAt]有了[默认值0]，导致[Hibernate最终生成的WHERE语句]意外变成了
	WHERE email = ? AND password = ? AND createdAt = 0

显然，[额外的查询条件]将导致[错误的查询结果]。

PS:  使用 findByExample()时，注意[基本类型Field]总是会加入到[WHERE条件]！


=========================================================


#	使用Criteria查询
Criteria  n.  标准,准则,原则

第2种查询方法是:
	使用 Criteria查询

可以实现如下： */
public User login(String email, String password) {

	DetachedCriteria criteria = DetachedCriteria.forClass(User.class);
	// Detached  adj. 单独的;独立的;不连接的;  v. 拆卸,分开,派遣
	// DetachedCriteria  分离标准

	criteria.add(Restrictions.eq("email", email))
			.add(Restrictions.eq("password", password));
		// Restrictions  n.  限制规定;限制法规;限制;约束;制约因素

	List<User> list = (List<User>) hibernateTemplate.findByCriteria(criteria);

	return list.isEmpty() ? null : list.get(0);
}

/*
DetachedCriteria使用[链式语句]来添加[多个AND条件]。

和 findByExample() 相比, findByCriteria() 可以组装出[更灵活的WHERE条件]，例如：

	Select * From user Where (email = ? OR name = ?) AND password = ?


[上述查询]没法用findByExample()实现，但用[Criteria查询]可以实现如下： */
DetachedCriteria criteria = DetachedCriteria.forClass(User.class);

criteria.add(
	Restrictions.and(
		Restrictions.or(
			Restrictions.eq("email", email),
			Restrictions.eq("name", name)
		),
		Restrictions.eq("password", password)
	)
);

/*
只要组织好[Restrictions的嵌套关系]，[Criteria查询]可以实现[任意复杂的查询]。


======================================================


#	使用HQL查询

[最后一种]常用的查询是:
	直接编写[Hibernate内置的"HQL查询"]： */

List<User> list = (List<User>) hibernateTemplate.find(
	"FROM User WHERE email=? AND password=?", 
	email, password
);

/*
和SQL相比，[HQL]使用[类名]和[属性名]

由[Hibernate]自动转换为[实际的表名]和[列名]。

[详细的HQL语法]可以参考[Hibernate文档]。

--------------------

除了可以直接传入[HQL字符串]外，Hibernate还可以使用[一种NamedQuery]，它给[查询]起个[名字]，然后【保存在"注解"中】。

使用[NamedQuery]时，我们要先在【User类】标注： */
@NamedQueries(
	@NamedQuery(
		// 查询名称
		name = "login", 

		// 查询语句
		query = "Select u From User u WHERE u.email = ?0 AND u.password=?1"
	)
) 


/*
注意到[引入的NamedQuery]是[javax.persistence.NamedQuery]

它和[直接传入HQL]有点【不同】的是: 
	[占位符]使用 "?0" 、"?1"
	并且[索引]是从[0]开始的（真乱）。


使用[NamedQuery]只需要引入"查询名"和"参数"： */

@Entity
public class User extends AbstractEntity {
	//...

	public User login(String email, String password) {
		List<User> list = (List<User>) hibernateTemplate.findByNamedQuery(
			"locin", 
			email, password
		);

		return list.isEmpty() ? null : list.get(0);
	}
}

/*
直接写[HQL]和[使用NamedQuery]各有优劣。

[前者HQL]可以在【代码中直观地看到查询语句】
[后者NamedQuery]可以在【User类】统一管理【所有相关查询】。


======================================================

#	使用Hibernate原生接口

如果要【使用Hibernate原生接口】，但不知道怎么写，可以【参考HibernateTemplate的源码】。

【使用Hibernate的原生接口】实际上总是【从SessionFactory出发】，它通常用【全局变量存储】，在【HibernateTemplate】中以【成员变量】被注入。

有了[SessionFactory]，[使用Hibernate]用法如下： */
void operation() {
	Session session = null;
	boolean isNew = false;

	// 获取当前Session / 打开新的Session
	try {
		session = this.sessionFactory.getCurrentSession();
	} catch (HibernateException e) {
		session = this.sessionFactory.openSession();
		isNew = true;
	}
}


/*==================================================

#	练习

集成Hibernate  */


/*=================================================

#	----- 集成Hibernate の 小结 ----- 

1. 在[Spring]中【集成Hibernate】需要【配置的Bean】如下：
	· DataSource；
	· LocalSessionFactory；
	· HibernateTransactionManager；
	· HibernateTemplate（推荐）。


2. 推荐【使用Annotation】配置【所有的Entity Bean】。


--------------------

汇总:

1. 启用Hibernate */
public class AppConfig {

    @Bean
    LocalSessionFactoryBean createSessionFactory(@Autowired DataSource dataSource) {

        var props = new Properties();

        props.setProperty("hibernate.hbm2ddl.auto", "update"); // 生产环境不要使用
        props.setProperty("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
        props.setProperty("hibernate.show_sql", "true");


        var sessionFactoryBean = new LocalSessionFactoryBean();

        sessionFactoryBean.setDataSource(dataSource);
        sessionFactoryBean.setPackagesToScan("com.itranswarp.learnjava.entity");
        sessionFactoryBean.setHibernateProperties(props);

        return sessionFactoryBean;
    }

    @Bean
    HibernateTemplate createHibernateTemplate(@Autowired SessionFactory sessionFactory) {
        return new HibernateTemplate(sessionFactory);
    }

    @Bean
    PlatformTransactionManager createTxManager(@Autowired SessionFactory sessionFactory) {
        return new HibernateTransactionManager(sessionFactory);
    }
}

/*数据表 User
----------------------
CREATE TABLE user
    id BIGINT NOT NULL AUTO_INCREMENT,
    email VARCHAR(100) NOT NULL,
    password VARCHAR(100) NOT NULL,
    name VARCHAR(100) NOT NULL,
    createdAt BIGINT NOT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `email` (`email`)
);
--------------------------

JavaBean User表 如下 */
public class User {
    private Long id;
    private String email;
    private String password;
    private String name;
    private Long createdAt;

    // getters and setters
    ...
}


/*
2. Hibernate如何把 class User 映射到 [User表记录] */
@Entity
@Table(name="users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(nullable = false, updatable = false)
    public Long getId() { ... }

    @Column(nullable = false, unique = true, length = 100)
    public String getEmail() { ... }

    @Column(nullable = false, length = 100)
    public String getPassword() { ... }

    @Column(nullable = false, length = 100)
    public String getName() { ... }

    @Column(nullable = false, updatable = false)
    public Long getCreatedAt() { ... }
}

/*
[Book表] */
@Entity
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(nullable = false, updatable = false)
    public Long getId() { ... }

    @Column(nullable = false, length = 100)
    public String getTitle() { ... }

    @Column(nullable = false, updatable = false)
    public Long getCreatedAt() { ... }
}


/*
提取 class Book 和 class User 到 [一个抽象类 AbstractEntity] : */
@MappedSuperclass // @MappedSuperclass 表示 它用于继承
public abstract class AbstractEntity {

    private Long id;
    private Long createdAt;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(nullable = false, updatable = false)
    public Long getId() { ... }

    @Column(nullable = false, updatable = false)
    public Long getCreatedAt() { ... }

// Transient adj. 临时的,瞬间的
    @Transient // 返回一个“虚拟”的属性, 即计算出来
    public ZonedDateTime getCreatedDateTime() {
        return Instant.ofEpochMilli(this.createdAt).atZone(ZoneId.systemDefault());
    }

    @PrePersist // 预存留, 将[一个JavaBean]持久化到[数据库]之前（即执行INSERT语句）
    public void preInsert() {
        setCreatedAt(System.currentTimeMillis());
    }
}

/*
实例化 @Entity User  */
@NamedQueries(
    @NamedQuery(
        // 查询名称:
        name = "login",

        // 查询语句:
        query = "SELECT u FROM User u WHERE u.email=?0 AND u.password=?1"
    )
)
@Entity
public class User extends AbstractEntity {

    @Column(nullable = false, unique = true, length = 100)
    public String getEmail() { ... }

    @Column(nullable = false, length = 100)
    public String getPassword() { ... }

    @Column(nullable = false, length = 100)
    public String getName() { ... }
}


/*
编写[一个UserService]，[注入HibernateTemplate]以便简化代码： */
@Component
@Transactional
public class UserService {
    @Autowired
    HibernateTemplate hibernateTemplate;

    // Insert操作
    public User register(String email, String password, String name) {
        // 创建一个User对象:
        User user = new User();

        // 设置好各个属性:
        user.setEmail(email);
        user.setPassword(password);
        user.setName(name);

        // 不要设置id，因为使用了自增主键
        // 保存到数据库:
        hibernateTemplate.save(user);

        // 现在已经自动获得了id:
        System.out.println(user.getId());

        return user;
    }

    // Delete操作
    public boolean deleteUser(Long id) {
        User user = hibernateTemplate.get(User.class, id);

        if (user != null) {
            hibernateTemplate.delete(user);
            return true;
        }

        return false;
    }

    // Update操作
    public void updateUser(Long id, String name) {
        User user = hibernateTemplate.load(User.class, id);

        user.setName(name);

        hibernateTemplate.update(user);
    }

    // 使用Example查询
    public User login_Example(String email, String password) {
        User example = new User();

        example.setEmail(email);
        example.setPassword(password);

        List<User> list = hibernateTemplate.findByExample(example);

        return list.isEmpty() ? null : list.get(0);
    }

    // Criteria查询
    public User login_Criteria(String email, String password) {
        DetachedCriteria criteria = DetachedCriteria.forClass(User.class);

        criteria.add(Restrictions.eq("email", email))
                .add(Restrictions.eq("password", password));

        List<User> list = (List<User>) hibernateTemplate.findByCriteria(criteria);

        return list.isEmpty() ? null : list.get(0);
    }

    // HQL查询
    public User login_HQL(String email, String password) {
    	List<User> list = (List<User>) hibernateTemplate.find(
    		"FROM User WHERE email=? AND password=?", 
    		email, password
    	);
    }

   	// NamedQuery查询
   	public User login_NamedQuery(String email, String password) {
   	    List<User> list = (List<User>) hibernateTemplate.findByNamedQuery(
   	    	"login", 
   	    	email, password
   	    );

   	    return list.isEmpty() ? null : list.get(0);
   	}

   	// 使用 Hibernate原生API 查询
   	void operation() {

   	    Session session = null;
   	    boolean isNew = false;

   	  // 获取当前Session或者打开新的Session:
   	    try {
   	        session = this.sessionFactory.getCurrentSession();
   	    } catch (HibernateException e) {
   	        session = this.sessionFactory.openSession();
   	        isNew = true;
   	    }

   	  // 操作Session:
   	    try {
   	        User user = session.load(
   	        	User.class, 123L
   	        );
   	    }
   	    finally {
   	        // 关闭[新打开的Session]
   	        if (isNew) { // 如果打开了, isNew = true
   	            session.close();
   	        }
   	    }

   	}
}







